/*********************************************************************************
Organization 					: 				Georgia Institute of Technology
  
Institute 						:				Cognitive Computing Group(CCL)
 
Authors							: 				Neha Sgandh, Santiago Ontanon
 												
Class							:				Case Base
 
Function						: 				The class contains the Plan library. 
												It provides helper function to load 
												the cases from the XML file
*********************************************************************************/


package mre;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.jdom.Element;
import org.jdom.input.SAXBuilder;

import planninglanguage.conditions.AttemptLimitCondition;
import planninglanguage.conditions.Condition;
import planninglanguage.conditions.NotCondition;
import planninglanguage.conditions.PlayerExistsCondition;
import planninglanguage.conditions.TimeOutCondition;
import planninglanguage.goals.WinWargusGoal;
import planninglanguage.plans.BasicOperatorPlan;
import planninglanguage.plans.Plan;
import planninglanguage.plans.SubGoalPlan;
import tacticalLayer.TacticalLayer;
import util.XMLWriter;
import base.CBLILRModule;
import base.ProxyBot;
import base.WargusGameState;
import base.WargusStateImporter;
import base.WargusUnit;
import cbr.CBR;

public class MRE extends CBLILRModule {
	private static final int ATTEMPT_LIMIT = 3;
	private static final int ACTION_START_LIMIT = 3000;
	public static final int ACTION_SUCCESS_LIMIT = 4000;
	public static final int BEHAVIOR_SUCCESS_LIMIT = 2000;
	private static final int RESTARTCYCLE = 50000;
	private static final int MAX_CYCLES = 11;
	
	private int current_cycle = 0;
	
	private WargusGameState m_currentGameState = null;

	private ProxyBot m_pb;
	private SubGoalPlan m_sgp;
	private int m_selfPlayer = 0;
	private LinkedList<SubGoalPlan> openGoals;
	private LinkedList<BasicOperatorPlan> executableActions;
	private LinkedList<BasicOperatorPlan> tentativeActions;
	private CBR m_cbr;
	private TacticalLayer m_tactical;
	
	// Behavior Modification data:
//	Reasoner m_reasoner;
//	Trace m_trace;
	
    private static PrintStream m_logger = null;	
    private static PrintStream m_experimentsLogger = null;	
	
    public static LinkedList<ResourceAllocation> allocated_resources = new LinkedList<ResourceAllocation>();
    public static int allocated_gold = 0;
    public static int allocated_wood = 0;
    public static int allocated_oil = 0;

	private static final int DEBUG_LEVEL = 2;
	public static final void DEBUG(int lvl, Object msg) {
		if (DEBUG_LEVEL >= lvl) System.out.println(msg);
	}
	
	private static final boolean RECORDING = false;
	private SaveThread saver = null;
	
	private class SaveThread implements Runnable {
		private boolean pause = false, stop = false;
		private MRE m;
		public SaveThread(MRE _m) {
			m = _m;
		}

		public void stop() { stop = true; }
		public void pause() { pause = true; }
		public void resume() { pause = false; }

		public void run() {
			while (!stop) {
				String cbrStateFile = "cbr-state-" + new Date().getTime() + ".xml";
				try {
					DEBUG(2, "outputing cbr state file: " + cbrStateFile);
					m.writeStateToXML(cbrStateFile);
				} catch (IOException ioe) {
					System.err.println("SaveThread.run: couldn't save state!");
					ioe.printStackTrace();
				}
				while (pause) {
					Thread.yield();
				}
				try { // XXX could do it based on cycles instead of time..
					 // it would then implicity handle the game pausing..
					Thread.sleep(5000); // sleep 5 seconds between saves
				} catch (InterruptedException ie) {
					System.err.println("SaveThread.run: couldn't sleep!");
				}
			}
		}
	}
	
	
	public MRE(ProxyBot a_pb, CBR a_cbr, TacticalLayer tactical) throws InterruptedException, FileNotFoundException {
		DEBUG(1, "MRE: Constructor started...");
		
		m_cbr = a_cbr;
		m_tactical = tactical;
		m_pb = a_pb;
		
//		m_trace = new Trace();
//		m_reasoner = new Reasoner(m_trace,m_cbr);
		
		try {
		      m_logger = new PrintStream( new FileOutputStream(new File("log/MRE_log.txt")) );
		      Plan.m_logger = m_logger;
		      Condition.m_logger = m_logger;
		} catch (FileNotFoundException e) {
			m_logger = null;
			e.printStackTrace();
		}		
		
		try {
			m_experimentsLogger = new PrintStream( new FileOutputStream(new File("log/experiments_log.txt")) );
			a_cbr.m_experimentsLogger = m_experimentsLogger;
		} catch (FileNotFoundException e) {
			m_experimentsLogger = null;
			e.printStackTrace();
		}		
		
		current_cycle = 0;
		
		DEBUG(1, "MRE: Constructor finished.");
	}
	

    public void restart() throws InterruptedException
    {
    	if (RECORDING && saver != null) { saver.pause(); }
    	
		LinkedList<Integer> l_opp = new LinkedList<Integer>();
		l_opp.add(new Integer(1));
		WinWargusGoal l_wwg = new WinWargusGoal(0,l_opp);
		String maps[] = {"maps/NWTR-lite.pud.gz",
						 "maps/NWTR-lite2.pud.gz",
						 "maps/NWTR-lite3.pud.gz",
						 "maps/NWTR-lite4.pud.gz",
						 "maps/NWTR-lite5.pud.gz",
						 "maps/NWTR-lite6.pud.gz",
						 "maps/NWTR-lite7.pud.gz",
						 "maps/NWTR-lite8.pud.gz",
						 "maps/NWTR-lite9.pud.gz",
						 "maps/NWTR-lite10.pud.gz",
						 "maps/GoW-lite.pud.gz"};
		
		m_sgp = new SubGoalPlan(l_wwg,null,null,null);
		m_currentGameState = null;
		
		allocated_resources.clear();
		allocated_gold = 0;
		allocated_wood = 0;
		allocated_oil = 0;	

		m_sgp.resetPlanStatus(m_currentGameState);

		// Load a new map:
		{
//			Random generator = new Random();
//			String map = maps[generator.nextInt(11)];
			String map = maps[current_cycle%11];
//			String map = "maps/GoW-lite.pud.gz";
//			String map = "maps/NWTR-lite.pud.gz";
			DEBUG(1, "MRE: map selected: " + map);
			m_logger.println("MRE: map selected: " + map);
			m_experimentsLogger.println("MRE: map selected: " + map);
			m_pb.LoadMapAndRestartScenario(map);
		}
		m_pb.sendAndReceiveMessage("SPEED 500", false);
		
		Thread.sleep(3000);	// Give time to stratagus to reset
    }
    
	public void run() {
		
		DEBUG(1, "MRE started");
		
		try {
//			 m_trace.createStartEntry(WargusStateImporter.getGameState(m_pb), m_sgp, (double)m_pb.getCurrCycle(), 0.0);
		} catch(Exception e) {
			System.out.println("Exception: " + e.toString());
		}
		
		m_cbr.reset();

		m_pb.pause();
		{
			boolean restarted = false;
			do {
				if (!m_pb.connected()) {
					DEBUG(1, "MRE: still not connected to stratagus.");
					DEBUG(1, "MRE: retrying in 5 seconds...");
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
				} // if
	
				try {
					if (m_pb.connected()) {
						restart();
						restarted = true;
					}
				} catch(Exception e) {
					DEBUG(1, "MRE: Exception trying to restart the game... ");
					e.printStackTrace();
					System.exit(0);				
				}
				
			} while(!restarted); 
		}
		
		m_pb.GetMapState();
		try {
			m_pb.GetState();
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		m_pb.getResearch();
		m_pb.resume();
		
		// Start saving game and AI state
		if (RECORDING) {
			saver = new SaveThread(this);
			new Thread(saver).start();
		}
		
		while(!m_stopSignal) {
			try {
				
				boolean restart = false;
				
				// Check if we have to restart the game:
				if (m_currentGameState!=null) {
//					Condition win = new NotCondition(new PlayerExistsCondition(1));
					Condition loss = new NotCondition(new PlayerExistsCondition(0));

//					if (win.test(m_currentGameState)) {
					if (m_sgp.getStatus()==Plan.SUCCEEDED) {	// Only restart when the MRE realizes that the game is won!
						DEBUG(1, "MRE: Game Won!!!!");
						m_logger.println("MRE: Game Won!!!!");
						m_experimentsLogger.println("MRE: Game Won!!!!");
						restart = true;
					}					
					if (loss.test(m_currentGameState)) {
						DEBUG(1, "MRE: Game Lost!!!!");
						m_logger.println("MRE: Game Lost!!!!");
						m_experimentsLogger.println("MRE: Game Lost!!!!");
						restart = true;
					}
					if (m_pb.getCurrCycle() > RESTARTCYCLE) {
						DEBUG(1, "MRE: Game Drawn!!!!");
						m_logger.println("MRE: Game Drawn!!!!");
						m_experimentsLogger.println("MRE: Game Drawn!!!!");
						restart = true;						
					}
				}
					
		 		if (restart) {
		 			DEBUG(1, "");
		 			DEBUG(1, " + CYCLE " + current_cycle + " FINISHED +++++++++++++++++++++++++++++++++++++++++++++++++++++ ");
		 			DEBUG(1, "MRE: Restarting game!!!!");
					m_logger.println("");
					m_logger.println(" + CYCLE " + current_cycle + " FINISHED +++++++++++++++++++++++++++++++++++++++++++++++++++++ ");
					m_logger.println("MRE: Restarting game!!!!");
					m_experimentsLogger.println("");
					m_experimentsLogger.println(" + CYCLE " + current_cycle + " FINISHED +++++++++++++++++++++++++++++++++++++++++++++++++++++ ");
					m_experimentsLogger.println("MRE: Restarting game!!!!");
		 			current_cycle++;
					try {
						m_pb.pause();
												
						m_cbr.reset();
						m_cbr.save();

//						m_reasoner.reason(m_trace);
//						m_trace = new Trace();
//						m_reasoner.setTrace(m_trace);	
																	
						restart();

						m_pb.reset();
						m_pb.resume();
						if (RECORDING) saver.resume();
					} catch (Exception e) {
						e.printStackTrace();
					}
		 			
		 			if (current_cycle>=MAX_CYCLES) {
		 				m_pb.stop();
		 				Thread.sleep(1000);	// Give the proxybot some time to stop
		 				m_pb.Quit();
		 				m_stopSignal=true;
		 			}
				}
		 		
				DEBUG(1, " - " + m_pb.getCurrCycle()+ " -------------------------------------------------- ");
				m_logger.println(" - " + m_pb.getCurrCycle()+ " -------------------------------------------------- ");
				
				m_tactical.cycle();
				
				updateGameState();
				if (m_currentGameState!=null) {
					DEBUG(2, "MRE: Allocated resources, gold: " + allocated_gold + " wood: " + allocated_wood + " oil: " + allocated_oil );
					m_logger.println("MRE: Allocated resources, gold: " + allocated_gold + " wood: " + allocated_wood + " oil: " + allocated_oil );
					DEBUG(2, "MRE: Checking plan status...");
	
					m_sgp.updateStatus(m_currentGameState, m_pb.getCurrCycle(), m_cbr);
					
					expandGoals();										
					if (!executeActions()) {
						// Only execute tentative actions if no actions are executed.
						// This is to prevent more than one action per cycle.
						// More than one action per cycle might cause problems since the 
						// effects of the first action are not taken into account by the preconditions
						// of the subsequent actions.					
						executeTentativeActions();
					} // if 

					DEBUG(2, "MRE: Current Plan:");
					DEBUG(2, m_sgp);
					m_logger.println("MRE: Current Plan:");
					m_logger.println(m_sgp);
//					m_logger.println("MRE: Current Game State:");
//					m_logger.println(CBLILRModule.GenerateXML(m_currentGameState));

				} // if
									
				Thread.sleep(200);	// Give some CPU time to the other threads
			} catch (InterruptedException ie) {
				System.err.println("MRE: Interrupted exception during execution!");
				ie.printStackTrace();
				System.exit(0);
			} catch (Exception e) {
				System.err.println("MRE: Exception during the MRE execution!");				
				e.printStackTrace();
//				m_trace.dump();
				System.exit(0);
			}
		}
		if (RECORDING) saver.stop();
	}
	
	void expandGoals() throws Exception {
		openGoals = new LinkedList<SubGoalPlan>();
		m_sgp.getOpenGoals(openGoals);
		m_sgp.updateParentHierarchy(null);
		DEBUG(2, "MRE: Number of ready open goals: "+openGoals.size());
		int l_numOpenGoals = openGoals.size();
		for(int i=0;i<l_numOpenGoals;i++) {
				
			SubGoalPlan l_g = openGoals.get(i);
			l_g.resetConditions(m_currentGameState);
			Condition c = l_g.getSuccessCondition();
			
			// Check if the goal is already achieved:
			if (c.test(m_currentGameState)) {
				l_g.setStatus(Plan.SUCCEEDED);
					
				// No Behavior adaptation trace entry is created, since no behavior was executed to achieve this
				// goal...
				
			} else {
			
				int attempts = l_g.getAttempts();
				if(attempts < ATTEMPT_LIMIT) {
					 Plan retrieved_plan;
					 if (l_g != m_sgp) l_g.incAttempt();
					 retrieved_plan=(Plan)m_cbr.retrieveCases(l_g.getGoal(),m_currentGameState);				 
					 
//					 if(attempts == 0) m_trace.createCBRIndexEntry(l_g.getGoal().getClass().getName(),m_currentGameState,l_g.getGoal());
					 l_g.setPlan(retrieved_plan);
					 retrieved_plan.updateParentHierarchy(l_g);
						
					 l_g.setStatus(Plan.READY);
//					 m_trace.createStartEntry(m_currentGameState, l_g, (double)m_pb.getCurrCycle(), 0.0);
									
					 l_g.getPlan().resetPlanStatus(m_currentGameState);
				 } else	 {
					 l_g.setStatus(Plan.FAILED);
					 
					 List<Condition> l = new LinkedList<Condition>();
					 l.add(new AttemptLimitCondition(ATTEMPT_LIMIT));
//					 m_trace.createFailureEntry(m_currentGameState, (Plan)l_g,m_pb.getCurrCycle(),l, Trace.FailureConditionType.PRECONDITION);
				 }
			}
		}		
	}

	
	boolean executeActions() throws Exception {
		executableActions = new LinkedList<BasicOperatorPlan>();
		m_sgp.getExecutableActions(executableActions);
		
		LinkedList<Integer> executingUnits = new LinkedList<Integer>();
		m_sgp.getExecutingIds(executingUnits);
		
		m_sgp.updateParentHierarchy(null);
		
		DEBUG(2, "MRE: Number of actions ready to be executed: "+executableActions.size());
		
		int l_numActions = executableActions.size();
		for(int i=0;i<l_numActions;i++)
		{
			 BasicOperatorPlan adapted_l_g,l_g_toadapt,l_g = executableActions.get(i);
			 l_g_toadapt= l_g;
			 while(l_g_toadapt.getOriginal()!=null && l_g_toadapt.getOriginal()!=l_g_toadapt) l_g_toadapt=l_g_toadapt.getOriginal();
			 
			 adapted_l_g = (BasicOperatorPlan)m_cbr.adaptPlan(l_g_toadapt, m_currentGameState,m_selfPlayer);
			 
			 if (adapted_l_g!=null) {
				 adapted_l_g.setOriginal(l_g_toadapt);
				 adapted_l_g.setParent(l_g.getParent());
				 
				 boolean unitFree = true;
				 int adaptedUnitID = adapted_l_g.getUnitID();
				 WargusUnit unit = m_currentGameState.getUnit(adaptedUnitID);
				 
				 if (unit!=null) {
					if (executingUnits.contains(new Integer(adaptedUnitID))) {
						DEBUG(2, "MRE: Action, the unit ID is being used!");
						unitFree = false;
					}

					DEBUG(2, "MRE: Action, the status of the adapted unit is " + WargusStateImporter.statusToString(unit.getStatus()[0]));
					if (WargusStateImporter.statusToString(unit.getStatus()[0]).equals("idle")) {
						unitFree = true;
					} 
					
				 }
				 
					
				 if (unitFree &&
					 (adapted_l_g.getPreCondition()==null || 
				      adapted_l_g.getPreCondition().test(m_currentGameState))) {
					 m_tactical.command(adapted_l_g);
					 DEBUG(2, "MRE: Action send for execution:");
					 DEBUG(2, adapted_l_g);
					 adapted_l_g.setStatus(Plan.EXECUTING);
					 adapted_l_g.setExecuteStartCycle(m_pb.getCurrCycle());
					 m_sgp.substitutePlan(l_g,adapted_l_g);
					 double delay = (l_g.getFirstExecutionCallCycle()==-1 ? 0:m_pb.getCurrCycle() - l_g.getFirstExecutionCallCycle());
					 m_pb.lockState();
					 adapted_l_g.setParent(l_g.getParent());
//					 m_trace.createStartEntry(WargusStateImporter.getGameState(m_pb), (Plan)adapted_l_g, m_pb.getCurrCycle(), delay);
					 m_pb.unlockState();
					 return true;
				 } else {
					 long l_firstExecutionTimeCycle = l_g.getFirstExecutionCallCycle();
					 if(l_firstExecutionTimeCycle==-1) {
						 DEBUG(2, "Setting the first call time");
						 l_g.setFirstExecutionCallCycle(m_pb.getCurrCycle());
					 } else {
						 long waitCycles = m_pb.getCurrCycle()- l_firstExecutionTimeCycle;
						 if(waitCycles>ACTION_START_LIMIT) {
							 l_g.setStatus(Plan.FAILED);
							 DEBUG(2, "MRE: preconditions of the basic action not met. Time out!");
							 
							 {
								 List<Condition> l = new LinkedList<Condition>();
								 l.add(new TimeOutCondition(ACTION_START_LIMIT,TimeOutCondition.TimeOutType.start));
//								 m_trace.createFailureEntry(m_currentGameState, (Plan)l_g,m_pb.getCurrCycle(),l, Trace.FailureConditionType.PRECONDITION);
							 }
						 } else {
							 if (!unitFree) DEBUG(2, "MRE: unit is not free.");
							 DEBUG(2, "MRE: preconditions of the basic action not met. Waiting... " + waitCycles + "/" + ACTION_START_LIMIT);
						 }
					 }
				 }
			 } else {
				 // Adaptation error!
				 DEBUG(2, "MRE: The CBR module couldn't adapt the plan!!!");
				 long l_firstExecutionTimeCycle = l_g.getFirstExecutionCallCycle();
				 if(l_firstExecutionTimeCycle==-1) {
					 DEBUG(2, "Setting the first call time");
					 l_g.setFirstExecutionCallCycle(m_pb.getCurrCycle());
				 } else {
					 long waitCycles = m_pb.getCurrCycle()- l_firstExecutionTimeCycle;
					 if(waitCycles>ACTION_START_LIMIT) {
						 l_g.setStatus(Plan.FAILED);
						 DEBUG(2, "MRE: Couldn't adapt the plan. Timed out!");						 
						 {
							 List<Condition> l = new LinkedList<Condition>();
							 l.add(new TimeOutCondition(ACTION_START_LIMIT,TimeOutCondition.TimeOutType.start));
//							 m_trace.createFailureEntry(m_currentGameState, (Plan)l_g,m_pb.getCurrCycle(),l, Trace.FailureConditionType.PRECONDITION);
						 }
					 } else {
						 DEBUG(2, "MRE: Couldn't adapt the plan. Waiting... " + waitCycles + "/" + ACTION_START_LIMIT);
					 }
				 }
			 }
		}
		return false;
	}

	
	void executeTentativeActions() throws Exception {
		tentativeActions = new LinkedList<BasicOperatorPlan> ();				
		m_sgp.getTentativeActions(tentativeActions);		
		m_sgp.updateParentHierarchy(null);
		LinkedList<Integer> executingUnits = new LinkedList<Integer>();
		m_sgp.getExecutingIds(executingUnits);
		int l_numTentativeActions = tentativeActions.size();
		DEBUG(2, "MRE: Number of tentative actions ready to be executed: "+tentativeActions .size());		
				
		for(int i=0;i<l_numTentativeActions;i++)
		{
			 BasicOperatorPlan adapted_l_g,l_g_toadapt,l_g = tentativeActions.get(i);
			 l_g_toadapt= l_g;
			 while(l_g_toadapt.getOriginal()!=null && l_g_toadapt.getOriginal()!=l_g_toadapt) l_g_toadapt=l_g_toadapt.getOriginal();
			 adapted_l_g = (BasicOperatorPlan)m_cbr.adaptPlan(l_g_toadapt, m_currentGameState,m_selfPlayer);
			 
			 if (adapted_l_g != null) {
				 adapted_l_g.setOriginal(l_g_toadapt);
				 int adaptedUnitID = adapted_l_g.getUnitID();
				 WargusUnit unit = m_currentGameState.getUnit(adaptedUnitID);
				 boolean unitFree = false;
				 adapted_l_g.setParent(l_g.getParent());
					
				 DEBUG(2, "MRE: tentativeAction, the selected ID is " + adaptedUnitID);
				 DEBUG(2, "MRE: tentativeAction, the previously used IDs are " + executingUnits);
				 
				 if (unit!=null) {
					DEBUG(2, "MRE: tentativeAction, the status of the adapted unit is " + WargusStateImporter.statusToString(unit.getStatus()[0]));
					if (WargusStateImporter.statusToString(unit.getStatus()[0]).equals("idle")) {
						unitFree = true;
					} 
				 }
				 
				 if (executingUnits.contains(new Integer(adaptedUnitID))) {
					 DEBUG(2, "MRE: tentativeAction, the unit ID is being used!");
					 unitFree = false;
				 }
				 
				 if (unitFree) {
					 if (adapted_l_g.getPreCondition()==null || 
					     adapted_l_g.getPreCondition().test(m_currentGameState)) {		
						 m_tactical.command(adapted_l_g);
						 DEBUG(2, "MRE: Tentative action send for execution:");
						 DEBUG(2, adapted_l_g);
						 adapted_l_g.setStatus(Plan.EXECUTING);
						 adapted_l_g.setExecuteStartCycle(m_pb.getCurrCycle());
						 m_sgp.substitutePlan(l_g,adapted_l_g);
						 adapted_l_g.setParent(l_g.getParent());
						 double delay = (l_g.getFirstExecutionCallCycle()==-1 ? 0:m_pb.getCurrCycle() - l_g.getFirstExecutionCallCycle());
						 m_pb.lockState();
//						 m_trace.createStartEntry(WargusStateImporter.getGameState(m_pb), (Plan)adapted_l_g, m_pb.getCurrCycle(), delay);
						 m_pb.unlockState();
						 break;							 
						 
					 } else {
						 long l_firstExecutionTimeCycle =l_g.getFirstExecutionCallCycle();
						 if(l_firstExecutionTimeCycle==-1)
						 {
							 DEBUG(2, "Setting the first call time");
							 l_g.setFirstExecutionCallCycle(m_pb.getCurrCycle());		 
						 }
						 else
						 {
							 long waitCycles = m_pb.getCurrCycle()-l_firstExecutionTimeCycle;
							 if(waitCycles>ACTION_START_LIMIT)
							 {
								 l_g.setStatus(Plan.FAILED);
								 DEBUG(2, "MRE: preconditions of the basic action not met. Timed out!");
	
								 {
									 List<Condition> l = new LinkedList<Condition>();
									 l.add(new TimeOutCondition(ACTION_START_LIMIT,TimeOutCondition.TimeOutType.start));
//		 							 m_trace.createFailureEntry(m_currentGameState, (Plan)l_g,m_pb.getCurrCycle(),l, Trace.FailureConditionType.PRECONDITION);
								 }
							 }
							 else
								 DEBUG(2, "MRE: preconditions of the basic action not met. Waiting... "+waitCycles + "/" + ACTION_START_LIMIT);
						 }
					 }
				 } else {
					 DEBUG(2, "MRE: unit for tentative action is not free. Waiting. ");
				 }
			 } else {
				 // Adaptation error!
				 DEBUG(2, "MRE: The CBR module couldn't adapt the plan!!! Since it's just a tentative action, just wait");
			 }
		}		
	}
	
	private synchronized void updateGameState()throws Exception
	{
		m_currentGameState = m_tactical.getCurrentGameState();		
		if (m_currentGameState==null) return;

		// Update Resource allocations:
		// If a building is started, deallocate resources:
		LinkedList<ResourceAllocation> tmp_allocated_resources = new LinkedList<ResourceAllocation>();
	
		for(WargusUnit unit:m_currentGameState.getPlayer().getUnits()) {			
			tmp_allocated_resources.addAll(allocated_resources);
			
			allocated_resources.clear();
			
			while(tmp_allocated_resources.size()>0) {
				ResourceAllocation r = tmp_allocated_resources.remove();
				if (unit.getLocX() == r.m_x &&
					unit.getLocY() == r.m_y &&
					WargusStateImporter.unitSize(unit.getType())>1) {
					DEBUG(2, "MRE: Deallocatig resoures for: " + WargusStateImporter.unitTypeToString(unit.getType()));
					allocated_gold -= WargusStateImporter.unitCostGold(r.m_building);
					allocated_wood -= WargusStateImporter.unitCostWood(r.m_building);
					allocated_oil -= WargusStateImporter.unitCostOil(r.m_building);
				} else {
					allocated_resources.add(r);
				}
			}
		}		
		
		// If a unit with allocated resources is killed, then deallocate the resources:
		{
			LinkedList<ResourceAllocation> todelete = new LinkedList<ResourceAllocation>();
			
			for(ResourceAllocation r:allocated_resources) {
				WargusUnit unit = m_currentGameState.getUnit(r.m_unitID);
				
				if (m_currentGameState == null) {
					DEBUG(2, "MRE: Deallocatig resoures for unit " + r.m_unitID + " since it doesnot exist");
					todelete.add(r);
				} else {
					if (m_pb.getCurrCycle()>r.m_cycle+100 &&	// Give some time for the command to take place
						!WargusStateImporter.statusToString(unit.getStatus()[0]).equals("working")) {
						todelete.add(r);
						DEBUG(2, "MRE: Deallocatig resoures for unit " + r.m_unitID + " since it is not working, but: " + WargusStateImporter.statusToString(unit.getStatus()[0]));
					}
				}
			} // for
			
			while(todelete.size()>0) {
				ResourceAllocation r = todelete.remove();
				allocated_resources.remove(r);
				allocated_gold -= WargusStateImporter.unitCostGold(r.m_building);
				allocated_wood -= WargusStateImporter.unitCostWood(r.m_building);
				allocated_oil -= WargusStateImporter.unitCostOil(r.m_building);
			}
		}
	}

	public void writeStateToXML(String filename) throws IOException {
		// tell Wargus to save its state
		m_pb.GetSavedFileName();
		
		// now save the cbr state
		ZipOutputStream zipper = new ZipOutputStream(new FileOutputStream(filename + ".zip"));
		zipper.putNextEntry(new ZipEntry(filename));
		XMLWriter w = new XMLWriter(new PrintWriter(zipper));
		
		w.tag("cbr-state");
		{
			w.tag("game-state-file", m_pb.GetSaveMapFileName());
			
			// write the tactical layer
			m_tactical.writeXML(w);
			
			w.tag("strategic-layer");
			{
				m_sgp.writeXML(w);
				w.tag("gold", allocated_gold);
				w.tag("wood", allocated_wood);
				w.tag("oil", allocated_oil);
				
				w.tag("resource-allocations");
				{
					for (ResourceAllocation r : allocated_resources) {
						r.writeXML(w);
					}
				}
				w.tag("/resource-allocations");
			}
			w.tag("/strategic-layer");
		}
		w.tag("/cbr-state");
		w.close();
		zipper.closeEntry();
		zipper.close();
	}

	public void loadStateFromXML(String filename) throws Exception {
		// No validation because I'm too lazy to write and maintain a schema.
		SAXBuilder sb = new SAXBuilder(false);
		ZipFile unzipper = new ZipFile(new File(filename + ".zip"));
		Element root = sb.build(
				unzipper.getInputStream(unzipper.getEntry(filename))).getRootElement();
		
		assert root.getName().equals("cbr-state") :
			"MRE.loadStateFromXML: Invalid XML root" +
			root.getName(); 
		
		// XXX reset all member variables?
		m_tactical.loadFromXML(root.getChild("tactical-layer"));

		
		Element strategicData = root.getChild("strategic-layer");
		
		allocated_gold = Integer.parseInt(strategicData.getChild("gold").getText());
		allocated_wood = Integer.parseInt(strategicData.getChild("wood").getText());
		allocated_oil = Integer.parseInt(strategicData.getChild("oil").getText());
		
		allocated_resources = new LinkedList<ResourceAllocation>();
		for (Object o : strategicData.getChild("resource-allocations").getChildren()) {
			Element r = (Element) o;
			allocated_resources.add(ResourceAllocation.loadFromXML(r));
		}
		
		m_sgp = SubGoalPlan.loadSGPFromXML(strategicData.getChild("subgoal-plan"));
		// rebuild the parent hierarchy among the plans
		m_sgp.updateParentHierarchy(null);
		
		String gameStateFile = root.getChildText("game-state-file");
		m_pb.LoadSavedFileName(gameStateFile);
	}

}